fix(components): a stacked resizable group gets a divider, not a 1px sliver - #3024
Merged
Conversation
…sliver `ui/resizable.tsx` is the react-resizable-panels **v3** Shadcn file: every stacked-group style on its handle is keyed on `data-[panel-group-direction=vertical]`. v4 — the installed major (4.12.2) — never emits that attribute; the only ones it writes are `data-group`, `data-panel`, `data-separator`, `data-disabled` and `data-testid`. So all seven variants plus `[&[data-panel-group-direction=vertical]>div]:rotate-90` were dead selectors, and a stacked divider kept the base `w-px`. Measured in a browser before the fix: the divider rendered 1px wide × 16px tall — its whole height coming from the grip — with a 4×16px `::after` drag target pinned to the group's left edge and an unrotated grip. Off the grip, the separator was not the hit target anywhere along the split, so 99% of the divider could not be grabbed. `ui/**` is a no-touch Shadcn-sync zone (AGENTS.md #7), so the fix is a wrapper in `custom/resizable.tsx` that re-keys those styles onto `aria-orientation`, which v4 *does* emit on the separator — the same attribute upstream Shadcn moved to when it regenerated this component for v4. The value reads backwards, deliberately: `aria-orientation` describes the separator, not the group, so an `orientation="vertical"` group (stacked panels) has an `aria-orientation="horizontal"` divider. `ui/index.ts` no longer re-exports `./resizable`; `custom/` owns the public `Resizable*` names, so no consumer can reach the unpatched handle through `@object-ui/components` (and the two barrels cannot collide on TS2308). The public export surface is unchanged. After, in a browser: 478×1px rule, 478×4px hit strip, grip at `rotate: 90deg`, and a real off-grip drag moves the split 50 → 75.21. Side-by-side is untouched (1×238px, unrotated grip). Affected surfaces: `FormSchema.fieldPanes` with `fieldPanesOrientation: 'vertical'` (#2153) and the SDUI `resizable` renderer with `direction: 'vertical'`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
Contributor
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
os-zhuang
added a commit
that referenced
this pull request
Jul 30, 2026
… rename to `orientation` (#3025) react-resizable-panels v4 renamed `PanelGroup`'s `direction` prop to `orientation`. Two call sites were still passing the v3 name, so v4 ignored it and React forwarded the unknown prop to the DOM as a stray `direction="horizontal"` attribute on the group div. Both groups are horizontal-only and `horizontal` is v4's default, so nothing rendered wrong — this is dead-prop cleanup, the tail of the migration whose visible half was #3024. Each site also carried `const PanelGroup = ResizablePanelGroup as React.FC<any>`, and those casts are why the dead prop survived the v4 bump: they erased the props to `any`, so `direction` type-checked against nothing. Both comments blamed the toolchain — vite-plugin-dts "not resolving the direction prop type correctly", and a prop that "does not always narrow cleanly in our TS config" — but neither was a narrowing problem. `GroupProps` simply has no `direction` key in v4. Dropping the casts makes the rename self-enforcing: restoring `direction` on the un-cast `ResizablePanelGroup` now fails with TS2322 rather than silently reaching the DOM. ResourceEditPage's `ResizablePanel` props are unaffected — `panelRef`, `collapsible`, `collapsedSize` and `onResize` are all valid v4 `PanelProps` and were never covered by the cast (it only ever wrapped the group), so no narrowly-typed workaround is needed. Co-authored-by: Jack Zhuang <277994282+os-zhuang@users.noreply.github.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
os-zhuang
added a commit
that referenced
this pull request
Jul 30, 2026
…p the sync from breaking the build, and finish the v4 migration in it (#3029) `ui/resizable.tsx` looks like a Shadcn-synced no-touch file (AGENTS.md #7). It is not one, and has not been since `00b61d6d8` ("upgrade shadcn components and fix build issues") hand-migrated its imports from v3's `import * as ResizablePrimitive` to v4's `{ Group, Panel, Separator }`. Upstream never followed. Both registry endpoints — `styles/default` and `styles/new-york` — still serve the v3 file, which reaches for `ResizablePrimitive.PanelGroup` and `PanelResizeHandle`. Neither name exists in react-resizable-panels v4.12.2: it exports only `Group`, `Panel` and `Separator`. So `pnpm shadcn:update-all` — an advertised script — overwrites this file with code that cannot compile. The commit title above is what that looks like after the fact; it has already happened once. Defused by listing `resizable` under `customComponents` in `shadcn-components.json`, which is the manifest's existing mechanism for this: `--update-all` iterates `Object.keys(manifest.components)`, and `--update resizable` now refuses with "not found in Shadcn registry". With the file no longer synced, the v4 fix belongs in it rather than in a wrapper built to avoid touching it. #3024 added `custom/resizable.tsx` to re-key the stacked-divider styles off the dead `data-[panel-group-direction=vertical]` and onto the `aria-orientation` attribute v4 actually emits — 8 compensating classes shadowing 8 dead ones in a file one import away. Those 8 now live in `ui/resizable.tsx` directly and `custom/resizable.tsx` is a plain re-export, kept as the sole public export site because `src/index.ts` star-exports `./ui` and `./custom` side by side and exporting `Resizable*` from both collides (TS2308). The group's `data-[panel-group-direction=vertical]:flex-col` is dropped outright: v4 writes `flex-direction: row | column` as an inline style off `orientation`, which outranks any class. Behaviour is unchanged, and provably so — diffing the merged class string before against after yields exactly the 8 dead selectors removed and nothing added, with `tailwind-merge` dropping none of the 23 survivors now that base and variants share one `cn()` call. Co-authored-by: Jack Zhuang <277994282+os-zhuang@users.noreply.github.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The bug
packages/components/src/ui/resizable.tsxis the react-resizable-panels v3 Shadcn file: every stacked-group style onResizableHandleis keyed ondata-[panel-group-direction=vertical]. The installed major is v4.12.2, which never emits that attribute — the only ones it writes aredata-group,data-panel,data-separator,data-disabledanddata-testid.So all seven variants (
h-px,w-full,after:left-0,after:h-1,after:w-full,after:-translate-y-1/2,after:translate-x-0) plus[&[data-panel-group-direction=vertical]>div]:rotate-90were dead selectors, and a stacked divider kept the basew-px. (ResizablePanelGroup'sdata-[…]:flex-colis dead too but harmless — the library writesflex-directionas an inline style.)Confirmed in a browser first
Rendered through the unpatched
ui/resizablein a zero-backend console Vite entry,orientation="vertical", in a 480px group:::afterdrag targetrotate: 90degdocument.querySelectorAll('[data-panel-group-direction]').lengthwas0for the whole document.Visually, before: no rule at all between the stacked panels — just an orphaned, unrotated grip floating at the far-left edge. After: a full-width hairline with the grip rotated and centred on it.
The fix
packages/components/src/ui/**is a no-touch Shadcn-sync zone (AGENTS.md #7), so this is a wrapper —packages/components/src/custom/resizable.tsx— that re-keys those styles ontoaria-orientation, which v4 does emit on the separator.Upstream Shadcn has since been regenerated for v4 and moved to exactly this attribute, so the class strings here match upstream rather than being hand-rolled. (Its file is otherwise a full Tailwind-v4/
data-slotgeneration that only 2 of this repo's 48ui/files have adopted, so a wholesale re-sync was not the smaller change.)The value reads backwards, deliberately —
aria-orientationdescribes the separator, not the group:ui/index.tsno longer re-exports./resizable;custom/owns the publicResizable*names. That keeps a single canonical handle (no consumer can reach the unpatched one through@object-ui/components) and avoids a TS2308 collision, sincesrc/index.tsstar-exports./uiand./customside by side. The public export surface is unchanged — verified by type-checking@object-ui/app-shellagainst a freshly builtdist, since it consumes all three names via the package barrel.Consumers repointed: the form renderer (
fieldPanes), the SDUIresizablerenderer, andcustom/navigation-overlay.tsx.Affected surfaces
FormSchema.fieldPaneswithfieldPanesOrientation: 'vertical'— found while fixing ModalForm 显式多 section 表单:每个 section 独立 <form>,提交时丢弃第 2+ 个 section 的值 #2153 (PR fix(form): a split form keeps BOTH panels' values (#2153) #3012,aa3556166), which movedSplitForm's panel group into the form renderer. Re-verified in a browser through the real form renderer: full-width rule, rotated grip.resizablerenderer withdirection: 'vertical'.containers.tsx'sisVerticalturned out not to be affected — it is Tabs orientation, not a panel group.Tests
packages/components/src/__tests__/resizable-orientation.test.tsx(5 tests) pins the class contract and the attribute it depends on, including a guard that asserts everyaria-[…]variant on the handle matches the real rendered DOM — the check that would have caught the original bug and that catches the next rename. Plus one case inform-field-panes.test.tsxtying it to the real consumer.Both mutations go red:
aria-[orientation=vertical](the plausible silent mistake — reading it as the group's orientation) → 4 failuresResolved geometry isn't asserted in vitest — happy-dom has no Tailwind — so that half is the browser verification above.
Verification
packages/components: 53 files / 410 tests passapp-shell/views/metadata-admin+plugin-grid+plugin-form: 176 files / 1526 tests passtype-checkclean for@object-ui/componentsand@object-ui/app-shelleslintclean on every changed file (0 errors; only pre-existing warnings inform.tsx)No changeset — pure bug fix, per AGENTS.md.
Noted, not fixed here
custom/navigation-overlay.tsx:466andapp-shell/.../ResourceEditPage.tsx:2064still pass the v3 prop namedirection="horizontal", which v4 ignores (it wantsorientation) and which leaks to the DOM as an attribute. Both are horizontal-only and horizontal is v4's default, so there is no visual bug today — but theas React.FC<any>casts at both sites, and the comment blamingvite-plugin-dtsfor "not resolving the direction prop type", are really this same half-finished v3→v4 migration. Left out to keep this PR to the visual defect.🤖 Generated with Claude Code